#!/bin/bash
# eslint pre-commit hook for git
readonly COMMIT_INTRODUCING_ESLINT='4a834d6976632b5ea535547172d95830dd5fc319'
readonly TMP_STAGING_DIR=$(mktemp -d)
readonly ESLINT_CONFIG_FILE='.eslintrc.js'

cleanup_temporary_directory() {
    rm -rf "${TMP_STAGING_DIR}"
}
trap cleanup_temporary_directory EXIT

get_list_of_files() {
    git diff --cached --name-only --diff-filter=ACMRTUXB | grep -E '\.(js|vue)$'
}

get_initial_commit() {
    local file=$1
    git log --diff-filter=A --format=format:%H "${file}" | tail -1
}

get_reference_source_file() {
    local file=$1
    git diff-index --cached HEAD ${file} | cut -d ' ' -f4
}

copy_staged_file_to_temporary_directory() {
    local file=$1
    local reference=$2
    mkdir -p "$TMP_STAGING_DIR/$(dirname ${file})"
    git cat-file blob ${reference} > "${TMP_STAGING_DIR}/${file}"
    echo "${TMP_STAGING_DIR}/${file}"
}

is_file_more_recent_than_js_coding_rule() {
    local file=$1
    local initial_file_commit
    initial_file_commit=$(get_initial_commit "${file}")
    if [ -z "${initial_file_commit}" ]
    then
        echo 1
    else
        git merge-base --is-ancestor "${initial_file_commit}" ${COMMIT_INTRODUCING_ESLINT}
        echo $?
    fi
}

get_list_of_mandatory_js_files() {
    local files=$1
    local file

    for file in ${files}
    do
        if [ "$(is_file_more_recent_than_js_coding_rule "${file}")" -ne 0 ]
        then
            local reference_file=$(get_reference_source_file "${file}")
            copy_staged_file_to_temporary_directory ${file} ${reference_file}
        fi
    done
}

# eslint glob-based overrides only apply to relative directory, so we copy the eslint config in the TMP dir
get_path_to_config() {
    cp ${ESLINT_CONFIG_FILE} ${TMP_STAGING_DIR}/${ESLINT_CONFIG_FILE}
    echo "${TMP_STAGING_DIR}/${ESLINT_CONFIG_FILE}"
}

main() {
    local files_list
    files_list=$(get_list_of_files)
    local files_js_mandatory
    files_js_mandatory=$(get_list_of_mandatory_js_files "${files_list}")
    if [[ -n ${files_js_mandatory} ]]
    then
        local path_to_config
        path_to_config=$(get_path_to_config)
        local eslint_output
        eslint_output=$(npm run eslint --silent -- --config ${path_to_config} ${files_js_mandatory})
        local status=$?
        if [ ${status} -ne 0 ]
        then
            echo "$eslint_output" | less
            local formatted_files=$(tr '\n' ' ' <<< $files_list)
            local error_message="
You can run the following command to let ESLint fix a part of the errors for you:

npm run eslint --silent -- --fix $formatted_files

"
            printf "$error_message"
        fi

        cleanup_temporary_directory
        exit ${status}
    fi
}
main
